package com.caissa.framework.starter.autoconfigure.db;


import com.alibaba.druid.stat.DruidStatManagerFacade;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.MeterBinder;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j
public class DruidDataSourceMetrics  implements MeterBinder {

    DruidStat druidStat ;

    @Override
    public void bindTo(MeterRegistry registry) {

        ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
        executorService.schedule(()->{
            DruidStatManagerFacade STAT_MANAGER = DruidStatManagerFacade.getInstance();
            List<Map<String, Object>> dataSourceStatDataList = STAT_MANAGER.getDataSourceStatDataList();

            if (dataSourceStatDataList.size() == 0) {
                log.warn("fail to expose datasource stat metric to prom");
                return;
            }

            log.info("found " + dataSourceStatDataList.size() + " datasource stat to register monitor prom");

//            dataSourceStatDataList.forEach((stat)->{
//            });

            druidStat = new DruidStat(dataSourceStatDataList.get(0));

            log.info("expose druid_active_count to prom");

            Gauge.builder("druid.active.count", druidStat, stat -> stat.getActiveCount())
                    .baseUnit("connections").register(registry);

            Gauge.builder("druid.maxactive.count", druidStat, stat -> stat.getMaxActive())
                    .baseUnit("connections").register(registry);

            Gauge.builder("druid.activepeek.count", druidStat, stat -> stat.getActivePeak())
                    .baseUnit("connections").register(registry);

            Gauge.builder("druid.error.count", druidStat, stat -> stat.getErrorCount())
                    .baseUnit("connections").register(registry);

            Gauge.builder("druid.discard.count", druidStat, stat -> stat.getDiscardCount())
                    .baseUnit("connections").register(registry);

            Gauge.builder("druid.execute.count", druidStat, stat -> stat.getExecuteCount())
                    .baseUnit("connections").register(registry);

            Gauge.builder("druid.rollback.count", druidStat, stat -> stat.getRollbackCount())
                    .baseUnit("connections").register(registry);

            Gauge.builder("druid.physicalConnect.count", druidStat, stat -> stat.getPhysicalConnectCount())
                    .baseUnit("connections").register(registry);

            Gauge.builder("druid.logicConnect.count", druidStat, stat -> stat.getLogicConnectCount())
                    .baseUnit("connections").register(registry);

            Gauge.builder("druid.pscache.hit.count", druidStat, stat -> stat.getPSCacheHitCount())
                    .baseUnit("connections").register(registry);

            Gauge.builder("druid.pscache.miss.count", druidStat, stat -> stat.getPSCacheMissCount())
                    .baseUnit("connections").register(registry);

            Gauge.builder("druid.waitthread.count", druidStat, stat -> stat.getWaitThreadCount())
                    .baseUnit("connections").register(registry);

            Gauge.builder("druid.recycle.error.count", druidStat, stat -> stat.getRecycleErrorCount())
                    .baseUnit("connections").register(registry);

        }, 15, TimeUnit.SECONDS);
    }

    static class DruidStat{

        Map<String, Object> stat;

        public DruidStat(Map<String, Object> stat) {
            this.stat = stat;
        }

        public int getActiveCount() {
            return stat.get("ActiveCount") != null ? (Integer) stat.get("ActiveCount") : 0;
        }

        public int getMaxActive() {
            return stat.get("MaxActive") != null ? (Integer) stat.get("MaxActive") : 0;
        }

        public int getActivePeak() {
            return stat.get("ActivePeak") != null ? (Integer) stat.get("ActivePeak") : 0;
        }

        public long getErrorCount() {
            return stat.get("ErrorCount") != null ? (Long) stat.get("ErrorCount") : 0;
        }

        public long getExecuteCount() {
            return stat.get("ExecuteCount") != null ? (Long) stat.get("ExecuteCount") : 0;
        }

        public long getDiscardCount() {
            return stat.get("DiscardCount") != null ? (Long) stat.get("DiscardCount") : 0;
        }

        public long getRollbackCount() {
            return stat.get("RollbackCount") != null ? (Long) stat.get("RollbackCount") : 0;
        }

        public long getPhysicalConnectCount() {
            return stat.get("PhysicalConnectCount") != null ? (Long) stat.get("PhysicalConnectCount") : 0;
        }
        
        public long getLogicConnectCount() {
            return stat.get("LogicConnectCount") != null ? (Long) stat.get("LogicConnectCount") : 0;
        }

        public long getPSCacheHitCount() {
            return stat.get("PSCacheHitCount") != null ? (Long) stat.get("PSCacheHitCount") : 0;
        }

        public long getPSCacheMissCount() {
            return stat.get("PSCacheMissCount") != null ? (Long) stat.get("PSCacheMissCount") : 0;
        }

        public int getWaitThreadCount() {
            return stat.get("WaitThreadCount") != null ? (Integer) stat.get("WaitThreadCount") : 0;
        }

        public long getRecycleErrorCount() {
            return stat.get("RecycleErrorCount") != null ? (Long) stat.get("RecycleErrorCount") : 0;
        }
    }
}
